package com.sukaiyi.giteeideaplugin.externcmd;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.io.IoUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Objects;
import java.util.StringTokenizer;
import java.util.concurrent.CompletableFuture;

public class CommandExecutor {

    private CommandExecutor() {

    }

    public static void exec(String command, CommandExecuteListener listener) {
        Objects.requireNonNull(command);
        Objects.requireNonNull(listener);
        InputStream is = null;
        try {
            StringTokenizer st = new StringTokenizer(command);
            String[] cmdArray = new String[st.countTokens()];
            for (int i = 0; st.hasMoreTokens(); i++)
                cmdArray[i] = st.nextToken();

            ProcessBuilder processBuilder = new ProcessBuilder(cmdArray);
            // 合并标准输出和错误输出
            processBuilder.redirectErrorStream(true);
            Process process = processBuilder.start();

            is = process.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));

            String line = null;
            while ((line = reader.readLine()) != null) {
                listener.onLine(line);
            }
            listener.onFinish(process.exitValue());
        } catch (IOException e) {
            e.printStackTrace();
            listener.onException(e);
        } finally {
            IoUtil.close(is);
        }
    }

    public static void execAsync(String command, CommandExecuteListener listener) {
        CompletableFuture.runAsync(() -> exec(command, listener));
    }

    public static CompletableFuture<CommandResult> execAsync(String command) {
        return CompletableFuture.supplyAsync(() -> {
            CommandResult result = new CommandResult();
            StringBuilder sb = new StringBuilder();
            exec(command, new CommandExecuteListener() {
                @Override
                public void onLine(String line) {
                    sb.append(line).append('\n');
                }

                @Override
                public void onFinish(int exitCode) {
                    result.setExitCode(exitCode);
                }

                @Override
                public void onException(Throwable e) {
                    throw ExceptionUtil.wrapRuntime(e);
                }
            });
            result.setOutput(sb.toString());
            return result;
        });
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static final class CommandResult {
        private Integer exitCode;
        private String output;
    }

    public interface CommandExecuteListener {
        void onLine(String line);

        void onFinish(int exitCode);

        void onException(Throwable e);
    }

}
